package anaglyphe;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.util.Enumeration;
import java.util.HashMap;

import javax.media.j3d.Alpha;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Locale;
import javax.media.j3d.Material;
import javax.media.j3d.OrderedGroup;
import javax.media.j3d.PhysicalBody;
import javax.media.j3d.PhysicalEnvironment;
import javax.media.j3d.PickInfo;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.media.j3d.View;
import javax.media.j3d.ViewPlatform;
import javax.media.j3d.VirtualUniverse;
import javax.media.j3d.WakeupOnBehaviorPost;
import javax.swing.JPanel;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import com.sun.j3d.utils.pickfast.PickCanvas;

//TODO : picking
//TODO : shape coloring
//TODO : Background

/**
 * Stereoscopic3D.java
 * 
 * Version: 0.5 Date: 2009/04/29
 * 
 * Copyright (c) 2009 August Lammersdorf, InteractiveMesh e.K. Kolomanstrasse
 * 2a, 85737 Ismaning Germany / Munich Area www.InteractiveMesh.com/org
 * 
 * Please create your own implementation. This source code is provided "AS IS",
 * without warranty of any kind. You are allowed to copy and use all lines you
 * like of this source code without any copyright notice, but you may not
 * modify, compile, or distribute this 'Stereoscopic3D.java'.
 * 
 */
final class Stereoscopic3D {

	static {
		System.out
				.println("Stereoscopic 3D : Copyright (c) 2009 August Lammersdorf, www.InteractiveMesh.com.");
	}

	private BoundingSphere globalBounds = null;

	private Locale locale = null;
	private View viewLeft = null;
	private View viewRight = null;
	private PhysicalBody physicalBodyLeft = null;
	private PhysicalBody physicalBodyRight = null;

	private JCanvas3DAnaglyph jCanvas3D = null;
	private JPanel parentPanel = null;

	private BranchGroup sceneBranch = null;
	private BranchGroup viewBranch = null;
	private BranchGroup enviBranch = null;

	private Background background = null;

	private TransformGroup positionGroup = null;
	private TransformGroup rotationGroup = null;
	private BranchGroup tuxBranch = null;

	private TransparencyAttributes knotTransAttr = null;

	private RotationInterpolator rotInterp = null;
	private Alpha rotationAlpha = null;
	private long rotationStartTime = 0;

	private OrbitBehaviorAnaglyph orbitBehAnaglyph = null;

	private PickCanvas pickCanvas = null;

	private int currCubeDim = 2;

	private Color bgColor0 = new Color(0, 102, 204); // RGB 0.0f, 0.4f, 0.8f,
	// HSB 209, 100, 80
	private Color bgColor1 = new Color(0, 153, 255); // RGB 0.0f, 0.6f, 1.0f,
	// HSB 203, 100, 100

	private VantagePointBehavior vpExecutor = null;

	private HashMap<String, VantagePoint> vantagepointHM = new HashMap<String, VantagePoint>();

	// Tux: shared geometries and appearances

	private GeometryArray bodyGeom = null;
	private GeometryArray frontGeom = null;
	private GeometryArray foot0Geom = null;
	private GeometryArray foot1Geom = null;
	private GeometryArray eye0Geom = null;
	private GeometryArray eye1Geom = null;
	private GeometryArray pup0Geom = null;
	private GeometryArray pup1Geom = null;
	private GeometryArray mouth0Geom = null;
	private GeometryArray mouth1Geom = null;

	private Appearance footAppear = null;
	private Appearance eyeAppear = null;
	private Appearance pupAppear = null;
	private Appearance mouthAppear = null;

	Stereoscopic3D() {
		initUniverse();
	}

	// Called from frame
	JPanel getJCanvas3D() {
		return parentPanel;
	}

	void closeUniverse() {
		viewLeft.removeAllCanvas3Ds();
		viewLeft.attachViewPlatform(null);
		viewRight.removeAllCanvas3Ds();
		viewRight.attachViewPlatform(null);
		locale.getVirtualUniverse().removeAllLocales();
	}

	//
	// Scene interaction
	//

	void setVantagePoint(String name) {
		VantagePoint vp = vantagepointHM.get(name);
		vpExecutor.setVP(vp);
		vpExecutor.postId(VantagePointBehavior.APPLY_VP);
	}

	void setCubeDimension(int dim) {
		if (currCubeDim == dim)
			return;
		currCubeDim = dim;
		tuxBranch.detach();
		createTuxBranch(currCubeDim);
	}

	void setKnotTransparency(int value) {
		knotTransAttr.setTransparency(value * 0.01f);
	}

	void resetRotation() {
		rotationAlpha.pause(rotationStartTime);
	}

	// Rotation speed and direction
	void setRotationSpeed(int speed) {
		if (rotationAlpha == null)
			return;

		if (speed > 99)
			speed = 99;
		else if (speed < 1)
			speed = 1;

		// Loop duration determines rotation speed
		long duration = 0;
		long pauseTime = 0;
		float pauseValue = 0;

		if (speed > 52) {

			// isPaused
			if (rotationAlpha.isPaused()) {

				if (rotationAlpha.getMode() == Alpha.INCREASING_ENABLE) {
					rotationAlpha.resume();
					return;
				}

				pauseTime = rotationAlpha.getPauseTime();
			}
			// Pause
			else {
				pauseTime = System.currentTimeMillis();
				rotationAlpha.pause(pauseTime);
			}

			pauseValue = rotationAlpha.value();

			if (rotationAlpha.getMode() != Alpha.INCREASING_ENABLE)
				rotationAlpha.setMode(Alpha.INCREASING_ENABLE);

			// New IncreasingAlphaDuration
			// duration = (long)1000 *((100 - speed)); // [47, 1] non linear
			duration = (long) (1500 * ((47.0 / (speed - 52)))); // [47, 1]
			// linear

			// Offset according to rotationAlpha's pauseValue and the new
			// IncreasingAlphaDuration
			long resumeOffsetTime = (long) (pauseValue * duration);

			rotationAlpha.setIncreasingAlphaDuration(duration);

			// Resume

			// Alpha source code: newStartTime = oldStartTime + resumeTime -
			// pauseTime
			// Start immediately and adapt new duration:
			// => System.currentTimeMillis() - resumeOffsetTime = oldStartTime +
			// resumeTime - pauseTime
			// => resumeTime = System.currentTimeMillis() - resumeOffsetTime -
			// oldStartTime + pauseTime

			long resumeTime = System.currentTimeMillis() - resumeOffsetTime
					- rotationAlpha.getStartTime() + pauseTime;

			rotationAlpha.resume(resumeTime);
		} else if (speed < 48) {

			// isPaused
			if (rotationAlpha.isPaused()) {

				if (rotationAlpha.getMode() == Alpha.DECREASING_ENABLE) {
					rotationAlpha.resume();
					return;
				}

				pauseTime = rotationAlpha.getPauseTime();
			}
			// Pause
			else {
				pauseTime = System.currentTimeMillis();
				rotationAlpha.pause(pauseTime);
			}

			pauseValue = rotationAlpha.value();

			if (rotationAlpha.getMode() != Alpha.DECREASING_ENABLE)
				rotationAlpha.setMode(Alpha.DECREASING_ENABLE);

			// New DecreasingAlphaDuration
			// duration = (long)(1000 * speed); // [47, 1] non linear
			duration = (long) (1500 * ((47.0 / (48 - speed)))); // [47, 1]
			// linear

			// Offset according to pauseValue and the new
			// DecreasingAlphaDuration
			long resumeOffsetTime = (long) ((1 - pauseValue) * duration);

			rotationAlpha.setDecreasingAlphaDuration(duration);

			// Resume
			rotationAlpha.resume(System.currentTimeMillis() - resumeOffsetTime
					- rotationAlpha.getStartTime() + pauseTime);
		} else {
			if (!rotationAlpha.isPaused()) {
				rotationAlpha.pause(System.currentTimeMillis());
			}
		}
	}

	//
	// Create universe
	//
	private void initUniverse() {

		createUniverse();
		createScene();

		createVantagePoints();

		loadTux();

		createTuxBranch(currCubeDim);

		setLive();

		createJCanvas3DOB();

		// Setup navigation
		updateOrbitBehavior();

	}

	private void updateOrbitBehavior() {

		double sceneRadius = 1.0f;

		Bounds bounds = sceneBranch.getBounds();
		BoundingSphere sphereBounds = null;

		if (bounds.isEmpty()) {
			sphereBounds = new BoundingSphere();
		} else {
			if (sphereBounds instanceof BoundingSphere)
				sphereBounds = (BoundingSphere) sceneBranch.getBounds();
			else
				sphereBounds = new BoundingSphere(sceneBranch.getBounds());
		}

		sceneRadius = sphereBounds.getRadius();

		orbitBehAnaglyph
				.setTransFactors(sceneRadius / 4.0f, sceneRadius / 4.0f);
		orbitBehAnaglyph.setZoomFactor(sceneRadius / 4.0f);
		orbitBehAnaglyph.setRotFactors(0.4f, 0.4f);

		orbitBehAnaglyph.setClippingBounds(sphereBounds);

		setVantagePoint("Front");

	}

	private void createScene() {

		// p51_mustang.obj / Mustang_Model.wrl ------>
		// Point3d lowerM = new Point3d(-6.68434, -1.96063, -8.49168);
		// Point3d upperM = new Point3d(7.43119, 2.53839, 8.49168);

		// Loader vrmlLoader = new VrmlLoader();

		// URL sceneUrl = null;
		BranchGroup mustangGroup = null;
		BranchGroup knotGroup = null;
		// try {
		// sceneUrl =
		// this.getClass().getResource("resources/Mustang_Model.wrl");
		// //mustangGroup = vrmlLoader.load(sceneUrl).getSceneGroup();
		//            
		// sceneUrl =
		// this.getClass().getResource("resources/GPUCapsViewer_Knot_scale.wrl");
		// //knotGroup = vrmlLoader.load(sceneUrl).getSceneGroup();
		// }
		// catch (FileNotFoundException e) {
		// e.printStackTrace();
		// }
		// catch (IncorrectFormatException e) {
		// e.printStackTrace();
		// }
		// catch (ParsingErrorException e) {
		// e.printStackTrace();
		// }
		// 

		knotTransAttr = new TransparencyAttributes();
		knotTransAttr.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
		knotTransAttr.setTransparencyMode(TransparencyAttributes.NICEST);
		// ((Appearance)((Shape3D)knotGroup.getChild(0)).getAppearance()).setTransparencyAttributes(knotTransAttr);

		TransformGroup mustangTG = new TransformGroup();
		Transform3D mustangT3D = new Transform3D();
		mustangT3D.setRotation(new AxisAngle4d(1, 0, 0, Math.toRadians(-90)));
		mustangT3D.setTranslation(new Vector3d(0, 0, 16));
		mustangTG.setTransform(mustangT3D);

		mustangTG.addChild(mustangGroup);

		TransformGroup mustangRotGroup = new TransformGroup();
		mustangRotGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

		// Cube parent

		positionGroup = new TransformGroup();
		positionGroup.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
		positionGroup.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
		positionGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

		// Rotation

		rotationGroup = new TransformGroup();
		rotationGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

		rotationGroup.addChild(positionGroup);
		rotationGroup.addChild(mustangTG);

		rotationAlpha = new Alpha();
		rotationAlpha.setLoopCount(-1);
		// Initialize rotation
		rotationAlpha.setIncreasingAlphaDuration(1500 * 47); // start speed =
		// min speed
		rotationStartTime = System.currentTimeMillis(); // start time
		rotationAlpha.setStartTime(rotationStartTime); // start
		rotationAlpha.pause(rotationStartTime); // pause at start time

		rotInterp = new RotationInterpolator(rotationAlpha, rotationGroup);
		rotInterp.setSchedulingBounds(globalBounds);

		OrderedGroup orderedGroup = new OrderedGroup();
		orderedGroup.addChild(rotationGroup);
		orderedGroup.addChild(knotGroup);

		sceneBranch.addChild(rotInterp);
		sceneBranch.addChild(orderedGroup);

	}

	private void createTuxBranch(int dim) {

		// Tux : 13.744 triangles, 10 Shape3Ds
		// dim 3 : 27 * 13.744 = 371.088

		// Tux BoundingBox
		Point3d lower = new Point3d(-0.81812, 0.01011, -0.31617);
		Point3d upper = new Point3d(0.81812, 1.45735, 0.28825);

		int xDist = 2;
		int yDist = 2;
		int zDist = 2;

		double halfX = ((upper.x - lower.x) + (dim - 1) * xDist) / 2;
		double halfY = ((upper.y - lower.y) + (dim - 1) * yDist) / 2;
		double halfZ = ((upper.z - lower.z) + (dim - 1) * zDist) / 2;

		Point3d center = new Point3d(lower.x + halfX, lower.y + halfY, lower.z
				+ halfZ);

		// Cube BoundingBox centered at (0, 0, 0)
		// Point3d lowerBox = new Point3d(-halfX, -halfY, -halfZ);
		// Point3d upperBox = new Point3d(halfX, halfY, halfZ);

		// Center cube at (0, 0, 0)
		Transform3D rotT3D = new Transform3D();
		rotT3D.setTranslation(new Vector3d(-center.x, -center.y, -center.z));
		positionGroup.setTransform(rotT3D);

		// Tux' branch
		tuxBranch = new BranchGroup();
		tuxBranch.setCapability(BranchGroup.ALLOW_DETACH);

		TransformGroup tuxTG = null;
		Transform3D tuxT3D = new Transform3D();

		float xTrans = 0;
		float yTrans = 0;
		float zTrans = 0;

		for (int i = 0; i < dim; i++) { // z axis

			// Appearances for body and front
			Appearance[] appears = createAppearances(dim, 1); // ((float)dim-i)/(float)dim
			int t = appears.length - 1;

			for (int j = 0; j < dim; j++) { // y axis
				for (int k = 0; k < dim; k++) { // x axis

					tuxTG = new TransformGroup();
					tuxT3D.setTranslation(new Vector3d(xTrans, yTrans, zTrans));
					tuxTG.setTransform(tuxT3D);

					tuxTG.addChild(new Shape3D(bodyGeom, appears[t]));
					tuxTG.addChild(new Shape3D(frontGeom, appears[t]));
					t--;

					tuxTG.addChild(new Shape3D(foot0Geom, footAppear));
					tuxTG.addChild(new Shape3D(foot1Geom, footAppear));

					tuxTG.addChild(new Shape3D(eye0Geom, eyeAppear));
					tuxTG.addChild(new Shape3D(eye1Geom, eyeAppear));

					tuxTG.addChild(new Shape3D(pup0Geom, pupAppear));
					tuxTG.addChild(new Shape3D(pup1Geom, pupAppear));

					tuxTG.addChild(new Shape3D(mouth0Geom, mouthAppear));
					tuxTG.addChild(new Shape3D(mouth1Geom, mouthAppear));

					tuxBranch.addChild(tuxTG);

					xTrans += xDist;
				} // x axis

				xTrans = 0;
				yTrans += yDist;
			} // y axis

			yTrans = 0;
			zTrans += zDist;

			t = appears.length - 1;
		} // z axis

		positionGroup.addChild(tuxBranch);
	}

	// Appearances for body and front
	private Appearance[] createAppearances(int dim, float bFactor) {

		Appearance[] appears = new Appearance[dim * dim];

		int k = 0;
		int direction = 1;
		float h = 0;

		float step = 1.0f / (dim * dim);

		for (int i = 0; i < dim; i++) {
			for (int j = 0; j < dim; j++) {

				appears[k] = new Appearance();

				float hAnaglyph = 0.5f + 0.16f * h;// 0.3333f + 0.3333f*h;
				appears[k].setMaterial(getMaterial(hAnaglyph, bFactor));

				h += step * direction;
				k++;
			}

			direction *= (-1);
			if (direction < 0)
				h += step * (dim - 1);
			else
				h += step * (dim + 1);
		}

		return appears;
	}

	private Material getMaterial(float hue, float bFactor) {
		Material mat = new Material();
		mat.setAmbientColor(0.0f, 0.0f, 0.0f);
		mat.setDiffuseColor(new Color3f(new Color(Color.HSBtoRGB(hue, 1.0f,
				0.8f * bFactor)))); // 0.85 0.7
		mat.setEmissiveColor(new Color3f(new Color(Color.HSBtoRGB(hue, 1.0f,
				0.2f * bFactor))));
		mat.setSpecularColor(0.2f, 0.2f, 0.2f);
		mat.setShininess(16f);
		return mat;
	}

	private void loadTux() {

		// URL sceneUrl =
		// this.getClass().getResource("resources/Tux1_Body.wrl");
		// if (sceneUrl == null) {
		// return;
		// }
		//        
		// String sceneUrlString = sceneUrl.toString();
		// String baseUrlString = sceneUrlString.substring(0,
		// sceneUrlString.lastIndexOf("/")+1);
		//        
		// URL baseUrl = null;
		// try {
		// baseUrl = new URL(baseUrlString);
		// }
		// catch (Exception e) {
		// e.printStackTrace();
		// }
		//        
		// Loader vrmlLoader = new VrmlLoader(); // No flag: Shapes only
		//        
		// // BaseUrl : just for fun, no texture etc. to load
		// vrmlLoader.setBaseUrl(baseUrl);
		//        
		// try {
		// Scene scene = null;
		// Shape3D shape3D = null;
		//            
		// // Tux1_Body.wrl
		// scene = vrmlLoader.load(sceneUrl);
		// bodyGeom =
		// (GeometryArray)((Shape3D)scene.getSceneGroup().getChild(0)).getGeometry();
		//    
		// sceneUrl = this.getClass().getResource("resources/Tux1_Front.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// frontGeom =
		// (GeometryArray)((Shape3D)scene.getSceneGroup().getChild(0)).getGeometry();
		//            
		// sceneUrl = this.getClass().getResource("resources/Tux1_Foot0.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// shape3D = (Shape3D)scene.getSceneGroup().getChild(0);
		// foot0Geom = (GeometryArray)shape3D.getGeometry();
		// footAppear = shape3D.getAppearance();
		//            
		// sceneUrl = this.getClass().getResource("resources/Tux1_Foot1.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// foot1Geom =
		// (GeometryArray)((Shape3D)scene.getSceneGroup().getChild(0)).getGeometry();
		//           
		// sceneUrl = this.getClass().getResource("resources/Tux1_Eye0.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// shape3D = (Shape3D)scene.getSceneGroup().getChild(0);
		// eye0Geom = (GeometryArray)shape3D.getGeometry();
		// eyeAppear = shape3D.getAppearance();
		//            
		// sceneUrl = this.getClass().getResource("resources/Tux1_Eye1.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// eye1Geom =
		// (GeometryArray)((Shape3D)scene.getSceneGroup().getChild(0)).getGeometry();
		//            
		// sceneUrl =
		// this.getClass().getResource("resources/Tux1_Eye0_Pup.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// shape3D = (Shape3D)scene.getSceneGroup().getChild(0);
		// pup0Geom = (GeometryArray)shape3D.getGeometry();
		// pupAppear = shape3D.getAppearance();
		//            
		// sceneUrl =
		// this.getClass().getResource("resources/Tux1_Eye1_Pup.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// pup1Geom =
		// (GeometryArray)((Shape3D)scene.getSceneGroup().getChild(0)).getGeometry();
		//           
		// sceneUrl =
		// this.getClass().getResource("resources/Tux1_MouthTop.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// shape3D = (Shape3D)scene.getSceneGroup().getChild(0);
		// mouth0Geom = (GeometryArray)shape3D.getGeometry();
		// mouthAppear = shape3D.getAppearance();
		//            
		// sceneUrl =
		// this.getClass().getResource("resources/Tux1_MouthBot.wrl");
		// scene = vrmlLoader.load(sceneUrl);
		// mouth1Geom =
		// (GeometryArray)((Shape3D)scene.getSceneGroup().getChild(0)).getGeometry();
		//            
		// }
		// catch (FileNotFoundException e) {
		// e.printStackTrace();
		// }
		// catch (IncorrectFormatException e) {
		// e.printStackTrace();
		// }
		// catch (ParsingErrorException e) {
		// e.printStackTrace();
		// }

	}

	/*
	 * private void changeRotationCenter(MouseEvent event) {
	 * 
	 * pickCanvas.setShapeLocation(event.getX(), event.getY()); PickInfo
	 * pickInfo = pickCanvas.pickClosest();
	 * 
	 * if (pickInfo != null) {
	 * 
	 * Shape3D pickedShape3D = (Shape3D)pickInfo.getNode();
	 * 
	 * Transform3D locToVWord = new Transform3D();
	 * pickedShape3D.getLocalToVworld(locToVWord);
	 * 
	 * Point3d rotationPoint = new Point3d();
	 * 
	 * Bounds bounds = pickedShape3D.getBounds();
	 * 
	 * if (bounds instanceof BoundingBox) { BoundingBox pickedBox =
	 * (BoundingBox)bounds;
	 * 
	 * Point3d lower = new Point3d(); Point3d upper = new Point3d();
	 * pickedBox.getLower(lower); pickedBox.getUpper(upper);
	 * 
	 * rotationPoint.set(lower.x + (upper.x - lower.x)/2, lower.y + (upper.y -
	 * lower.y)/2, lower.z + (upper.z - lower.z)/2); } else { BoundingSphere
	 * pickedSphere = null; if (bounds instanceof BoundingSphere) pickedSphere =
	 * (BoundingSphere)bounds; else pickedSphere = new BoundingSphere(bounds);
	 * 
	 * pickedSphere.getCenter(rotationPoint); }
	 * 
	 * locToVWord.transform(rotationPoint);
	 * orbitBehAnaglyph.setRotationCenter(rotationPoint, true); //
	 * isLookAtRotCenter } }
	 */
	// Set live
	private void setLive() {
		sceneBranch.compile();
		locale.addBranchGraph(sceneBranch);
		locale.addBranchGraph(viewBranch);
		locale.addBranchGraph(enviBranch);
	}

	// [0, 120]
	void setEyesPosition(int value) {
		double dist = value / (2.0 * 1000.0);
		// System.out.println("physicalBody setEyesPosition dist = " + dist);
		physicalBodyLeft.setRightEyePosition(new Point3d(dist, 0.0, 0.0));
		physicalBodyLeft.setLeftEyePosition(new Point3d(-dist, 0.0, 0.0));
		physicalBodyRight.setRightEyePosition(new Point3d(dist, 0.0, 0.0));
		physicalBodyRight.setLeftEyePosition(new Point3d(-dist, 0.0, 0.0));
	}

	// Base world
	private void createUniverse() {
		// Bounds
		globalBounds = new BoundingSphere();
		globalBounds.setRadius(Double.MAX_VALUE);
		//
		// Viewing
		//

		// Individual object for left and right due to interactive update in
		// 'setEyeDistance(int value)'
		physicalBodyLeft = new PhysicalBody();
		physicalBodyRight = new PhysicalBody();
		/*
		 * physicalBody.setLeftEyePosition(new Point3d(-0.033, 0.0, 0.0)); //
		 * (-0.033, 0.0, 0.0) physicalBody.setRightEyePosition(new
		 * Point3d(0.033, 0.0, 0.0)); // (0.033, 0.0, 0.0)
		 * 
		 * physicalBody.setNominalEyeHeightFromGround(1.68); // 1.68
		 * physicalBody.setNominalEyeOffsetFromNominalScreen(0.4572); // 0.4572
		 */
		PhysicalEnvironment pE = new PhysicalEnvironment();

		viewLeft = new View();
		viewLeft.setPhysicalBody(physicalBodyLeft);
		viewLeft.setPhysicalEnvironment(pE);

		viewRight = new View();
		viewRight.setPhysicalBody(physicalBodyRight);
		viewRight.setPhysicalEnvironment(pE);

		GraphicsConfigTemplate3D gCT = new GraphicsConfigTemplate3D();
		try {
			jCanvas3D = new JCanvas3DAnaglyph(gCT);
		} catch (Exception e) {
			System.out.println("Stereoscopic3D: JCanvas3DAnaglyph failed !!");
			e.printStackTrace();
			System.exit(0);
		}

		/*
		 * MouseAdapter jCanvas3D.addMouseListener(new MouseListener() { public
		 * void mouseClicked(MouseEvent event) { int clicks =
		 * event.getClickCount(); if (clicks == 2 &&
		 * SwingUtilities.isLeftMouseButton(event)) {
		 * changeRotationCenter(event); } } public void mouseEntered(MouseEvent
		 * e) {} public void mouseExited(MouseEvent e) {} public void
		 * mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e)
		 * {} });
		 */

		//
		// SuperStructure
		//
		VirtualUniverse vu = new VirtualUniverse();
		locale = new Locale(vu);
		//
		// BranchGraphs
		//
		sceneBranch = new BranchGroup();
		viewBranch = new BranchGroup();
		enviBranch = new BranchGroup();

		// ViewBranch

		TransformGroup viewTG = new TransformGroup();
		viewTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

		ViewPlatform viewsPlatform = new ViewPlatform();
		viewLeft.attachViewPlatform(viewsPlatform);
		viewRight.attachViewPlatform(viewsPlatform);

		orbitBehAnaglyph = new OrbitBehaviorAnaglyph(jCanvas3D, viewTG,
				viewLeft, viewRight, OrbitBehaviorAnaglyph.REVERSE_ALL);
		orbitBehAnaglyph.setSchedulingBounds(globalBounds);
		orbitBehAnaglyph.setClippingEnabled(true);
		orbitBehAnaglyph.setProjectionMode(View.PERSPECTIVE_PROJECTION);
		vpExecutor = new VantagePointBehavior(orbitBehAnaglyph);
		vpExecutor.setSchedulingBounds(globalBounds);

		DirectionalLight headLight = new DirectionalLight();
		headLight.setInfluencingBounds(globalBounds);

		viewTG.addChild(viewsPlatform);
		viewTG.addChild(orbitBehAnaglyph);
		viewTG.addChild(vpExecutor);
		viewTG.addChild(headLight);

		viewBranch.addChild(viewTG);

		// EnviBranch

		background = new Background();
		background.setCapability(Background.ALLOW_IMAGE_WRITE);
		background.setApplicationBounds(globalBounds);
		background.setColor(new Color3f(bgColor0));
		// Background image
		jCanvas3D.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				int width = jCanvas3D.getWidth();
				int height = jCanvas3D.getHeight();

				BufferedImage gradientImage = new BufferedImage(width, height,
						BufferedImage.TYPE_INT_RGB);
				Graphics2D g2d = (Graphics2D) gradientImage.getGraphics();

				// Still testing best mode: byRef & yUp (to avoid copying),
				// SCALE_NONE/SCALE_REPEAT !!??
				// Issue: NonPowerOfTwoSupport

				int splitHeight = (int) (height / 5f); // yUp !

				GradientPaint gp = new GradientPaint(0, 0, bgColor0, 0,
						splitHeight, bgColor1);
				g2d.setPaint(gp);
				g2d.fillRect(0, 0, width, splitHeight);

				gp = new GradientPaint(0, splitHeight, bgColor1, 0, height,
						bgColor0);
				g2d.setPaint(gp);
				g2d.fillRect(0, splitHeight, width, height);

				g2d.dispose();

				background
						.setImage(new ImageComponent2D(
								ImageComponent2D.FORMAT_RGB, gradientImage,
								true, true));

			}
		});

		enviBranch.addChild(background);
	}

	// Finishing JCanvas3D, create PickCanvas
	private void createJCanvas3DOB() {

		// Arbitrary size
		Dimension dim = new Dimension(100, 100);
		jCanvas3D.setPreferredSize(dim);
		jCanvas3D.setSize(dim);

		// Provide a parent !!
		parentPanel = new JPanel(new BorderLayout());
		parentPanel.add(jCanvas3D, BorderLayout.CENTER);

		// Optional: less 'flickering' during resizing
		jCanvas3D.setBackground(bgColor0);
		// TODO picking
		// Off-screen Canvas3D of this lightweight FXCanvas3D
		Canvas3D offCanvas3DLeft = jCanvas3D.getOffscreenCanvas3DLeft();
		Canvas3D offCanvas3DRight = jCanvas3D.getOffscreenCanvas3DRight();
		if (offCanvas3DLeft != null) {
			// View renders into the off-screen Canvas3D
			viewLeft.addCanvas3D(offCanvas3DLeft);
			viewRight.addCanvas3D(offCanvas3DRight);

			// PickCanvas operates on the not visible off-screen Canvas3D
			pickCanvas = new PickCanvas(offCanvas3DLeft, sceneBranch);
			pickCanvas.setMode(PickInfo.PICK_GEOMETRY);
			pickCanvas.setFlags(PickInfo.NODE);
			pickCanvas.setTolerance(4.0f);
		} else {
			System.out.println("Stereoscopic3D: OffscreenCanvas3D = null !!");
			System.exit(0);
		}
	}

	private void createVantagePoints() {

		vantagepointHM.clear();

		double fov = Math.toRadians(45); // default value is used

		// VantagePoints
		new VantagePoint("Front", new Point3d(0, 0, 50), new Point3d(),
				new Vector3d(0.0, 1.0, 0.0), new Point3d(), fov);
		new VantagePoint("Top", new Point3d(0, 50, 0), new Point3d(),
				new Vector3d(0.0, 0.0, -1.0), new Point3d(), fov);
	}

	private final class VantagePoint {

		private String name = null;

		private Point3d eyePerspect = new Point3d();
		private Point3d viewCenter = new Point3d();
		private Vector3d up = new Vector3d();
		private Point3d rotCenter = new Point3d();

		// private double fov = Math.PI/4;

		VantagePoint(String name, Point3d eyePer, Point3d viewCenter,
				Vector3d up, Point3d rotationCenter, double fov) {

			this.name = name;

			this.eyePerspect.set(eyePer);
			this.viewCenter.set(viewCenter);
			this.up.set(up);
			this.rotCenter.set(rotationCenter);
			// this.fov = fov;

			vantagepointHM.put(name, this);
		}

		@Override
		public String toString() {
			return name;
		}

		void applyTo(OrbitBehaviorAnaglyph navigator) {

			navigator.setViewingTransform(eyePerspect, viewCenter, up,
					rotCenter);

			// navigator.setFieldOfView(fov);
		}
	}

	// Sets vantage point in behavior scheduler
	private final class VantagePointBehavior extends Behavior {

		private static final int APPLY_VP = 1;
		private WakeupOnBehaviorPost postApply = new WakeupOnBehaviorPost(this,
				APPLY_VP);

		private OrbitBehaviorAnaglyph orbitBeh = null;
		private VantagePoint vantagePoint = null;

		VantagePointBehavior(OrbitBehaviorAnaglyph orbitBeh) {
			this.orbitBeh = orbitBeh;
		}

		// VantagePoint getVP() {
		// return vantagePoint;
		// }

		void setVP(VantagePoint vp) {
			vantagePoint = vp;
		}

		@Override
		public void initialize() {
			wakeupOn(postApply);
		}

		@SuppressWarnings("unchecked")
		@Override
		public void processStimulus(Enumeration criteria) {

			if (vantagePoint != null)
				vantagePoint.applyTo(orbitBeh);

			vantagePoint = null;
			wakeupOn(postApply);

		}
	}
}
